15. Exercise: Build a Repository

L9 27 Building Our Repository SC

Now it’s your turn to complete this exercise yourself.

Your Room database has been working out nicely. It’s saving all the Videos, and loading them. Saving and loading. It’s a pretty good database. Nice work.

Now we need you to implement a repository. You were just briefed on how the repository pattern can be used to implement offline caching - time to put it into practice.

A Repository is just a regular class that has one (or more) methods that load data without specifying the data source as part of the main API. Because it's just a regular class, there's no need for an annotation to define a repository. The repository hides the complexity of managing the interactions between the database and the networking code.

You can define any API that makes sense for your data. Make your repo take a VideosDatabase constructor parameter, this way you won’t have any dependencies on Context in your repository (so called dependency injection).

1. Create the repository class:

In VidesoRepository.kt, create the VideosRepository class.

class VideosRepository(private val database: VideosDatabase) { }

2. Define a refreshVideos() function to refresh the offline cache:

Make it a suspend function since it will be called from a coroutine.

suspend fun refreshVideos() {
}

For more on coroutines in Kotlin, check out the official guide.

3. Get the data from the network and then put it in the database:
In refreshVideos(), make a network call to getPlaylist(), and use the await() function to tell the coroutine to suspend until the data is available. Then call insertAll() to insert the playlist into the database.

 val playlist = Network.devbytes.getPlaylist().await()
 database.videoDao.insertAll(*playlist.asDatabaseModel())

Note the asterisk * is the spread operator. It allows you to pass in an array to a function that expects varargs.

4. Run your code on the IO Dispatcher:
Inside refreshVideos(), call withContext(Dispatchers.IO) to force the Kotlin coroutine to switch to the IO dispatcher.

withContext(Dispatchers.IO) {
     val playlist = Network.devbytes.getPlaylist().await()
     database.videoDao.insertAll(*playlist.asDatabaseModel())
}

Now let's go ahead and define the main interface for our video repository.

L9 28 Building Our Repository Part Two SC

5. Define a LiveData for your list of videos:

You want a LiveData of with a list of Videos. Video is the domain object. Room can return a LiveData of database objects called DatabaseVideo using the getVideos() method you wrote in the VideoDao. So you'll need to convert the list of DatabaseVideo to a list of Video. You have written a Kotlin extension function for this already in an earlier step, called asDomainModel().

Transformations.map is perfect for mapping the output of one LiveData to another type. If you need another refresher on Transformations.map(), you can check out the video in the "Exercise: LiveData Map Transformation" page of the "App Architecture (UI Layer)" lesson.

Use Transformation.map to convert your LiveData list of DatabaseVideo objects to domain Video objects. Your completed VideosRepository class should look like this:

class VideosRepository(private val database: VideosDatabase) {

   val videos: LiveData<List<Video>> = Transformations.map(database.videoDao.getVideos()) {
      it.asDomainModel()
   }

   suspend fun refreshVideos() {
       withContext(Dispatchers.IO) {
           val playlist = Network.devbytes.getPlaylist().await()
           database.videoDao.insertAll(*playlist.asDatabaseModel())
       }
   }
}

If you want to start at this step, you can download this exercise code from: Step.05-Exercise-Build-a-Repository.

You will find plenty of //TODO comments to help you complete this exercise, and if you get stuck go back and watch the video again.

Once you’re done, you can check your solution against the solution we’ve provided here Step.05-Solution-Build-a-Repository or using this git diff.

Task Description:

Check the steps below as you implement them to complete this exercise

Task List:

Task Feedback:

Great work! With your repository finished you’re ready to integrate it to complete your offline cache!